/*
 * This file is Copyright 2002-2006 Loyola University Chicago,
 * Department of Computer Science and the Emerging Technologies Laboratory.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License via
 *    http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.citep.tests.db.hibernate.sessions;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Date;
import java.math.BigDecimal;

import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;

import com.citep.business.sessions.*;
import com.citep.business.*;
import com.citep.db.hibernate.*;

public class AccountSessionTest extends AbstractTransactionalDataSourceSpringContextTests {

	private AccountSession session;

	private static int accountId;
	private static int accountCategoryId;
	
	private String accountName = "account name";
	private String accountDescription = "account description";
	
	private String categoryName = "category name";
	private String categoryDescription = "category description";

	public void setAccountSession(AccountSession s) {
		session = s;
	}

	protected String[] getConfigLocations() {
		return new String[] { "classpath:applicationContext.xml" };
	}


	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.createAccountCategory(String, String)'
	 * This test is not rolled back since we need an existing account category for future tests
	 */
	public void testCreateAccountCategoryStringString() {
		AccountCategory category = session.createAccountCategory(categoryName,categoryDescription);
		
		assertTrue(category.getId() > 0);
		assertEquals(category.getName(),categoryName);
		assertEquals(category.getDescription(),categoryDescription);
		
		accountCategoryId = category.getId();
		setComplete(); //do not rollback the transaction since we want to persist this object for future tests
	}
	
	/*
	 * Make sure we cannot add accounts with the same name 
	 */
	public void testCreateAccountDuplicateException() {
		
		try {
			AccountCategory category = new AccountCategoryDAO();
			category.setName(categoryName);
			category.setDescription(categoryDescription);
			session.createAccountCategory(category);
			fail("Did not receive validation exception for duplicate account");
		} catch(ValidationException e) {
			//a validationexception was thrown so everything's ok
		}
	}
	
	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.createAccountCategory(AccountCategory)'
	 */
	public void testCreateAccountCategoryAccountCategory() {
		AccountCategory category = new AccountCategoryDAO();
		category.setName(categoryName + "1");
		category.setDescription(categoryDescription + "1");
		
		session.createAccountCategory(category);
		
		assertTrue(category.getId() > 0);
	}


	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.getAccountCategory(int)'
	 */
	public void testGetAccountCategory() {
		AccountCategory retrieved = session.getAccountCategory(accountCategoryId);
		
		assertNotNull(retrieved);
		assertEquals(retrieved.getId(), accountCategoryId);
	}
	
	
	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.updateAccountCategory(AccountCategory)'
	 */
	public void testUpdateAccountCategory() {
		AccountCategory category = session.getAccountCategory(accountCategoryId);
		category.setName("category name 1");
		session.updateAccountCategory(category);

		assertEquals(category.getName(),"category name 1");
	}

	
	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.deleteAccountCategory(int)'
	 */
	public void testDeleteAccountCategoryInt() {
		AccountCategory category = session.getAccountCategory(accountCategoryId);
		session.deleteAccountCategory(category.getId());
	}

	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.deleteAccountCategory(AccountCategory)'
	 */
	public void testDeleteAccountCategoryAccountCategory() {
		AccountCategory category = session.getAccountCategory(accountCategoryId);
		session.deleteAccountCategory(category);
		
	}

	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.enableAccountCategory(int)'
	 */
	public void testEnableAccountCategory() {
		AccountCategory category = session.getAccountCategory(accountCategoryId);
		session.enableAccountCategory(category.getId());
		assertTrue(category.isEnabled());
	}

	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.disableAccountCategory(int)'
	 */
	public void testDisableAccountCategory() {
		AccountCategory category = session.getAccountCategory(accountCategoryId);
		session.disableAccountCategory(category.getId());
		assertFalse(category.isEnabled());
	}
	
	/*
	 * Test that when we create a new account under a certain category,
	 * the 'accounts' set under such category is updated as well.
	 * This is a three-test process.
	 * 
	 * The first tests creates the account and persists the information,
	 * allowing the session to close and recognize the dirty collection.
	 */
	public void testAccountCategoryIndirectAddAccount() {
		AccountCategory category = session.getAccountCategory(accountCategoryId);

		//add "1" to the account name to avoid a validation exception
		session.createAccount(accountName+"1",accountDescription,AccountTypes.ASSETS,new BigDecimal(1000),new Date(), new Date(),true,category);
		
		setComplete(); //do not rollback this since we need this for testAccountCategoryHasAccounts()
	}

	/*
	 * Warning! This test depends on testAccountCategoryAddAccount() running first.
	 * We cannot test this all on the previous test since we need the session to close
	 * so that all the dirty collections are flushed
	 * 
	 * Check that when we retrieve accountCategory, the collection reflects the previously added account
	 */
	public void testAccountCategoryHasAccounts() {
		AccountCategory category = session.getAccountCategory(accountCategoryId);
		assertTrue(category.getAccounts().size() == 1);

		accountId = category.getAccounts().iterator().next().getId();
	}
	

	public void testAccountCategoryRemoveAccount() {
		Account account = session.getAccount(accountId);
		session.deleteAccount(account);
		setComplete();
	}
	
	/*
	 * Warning! This test depends on testAccountCategoryAddAccount() and 
	 * testAccountCategoryHasAccounts() running first.
	 * Check that the previous test effectively removed the account from the collection
	 */
	public void testAccountCategoryHasNoAccounts() {
		AccountCategory category = session.getAccountCategory(accountCategoryId);
		assertTrue(category.getAccounts().size() == 0);
	}
	
	/*
	 * Test that adding an account to the category array works as expected
	 */
	public void testAccountCategoryDirectAddAccount() {
		AccountCategory category = session.getAccountCategory(accountCategoryId);
		category.getAccounts().add(session.createAccount(accountName+"1",accountDescription,AccountTypes.EQUITY,new BigDecimal(1000),new Date(), new Date(),true,category));
		session.updateAccountCategory(category);
	}

	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.getAccountCategories()
	 */
	public void testGetAccountCategories() {
		ArrayList<AccountCategory> list = new ArrayList<AccountCategory>();
		
		list.add(session.getAccountCategory(accountCategoryId));
		list.add(session.createAccountCategory(categoryName+"1",categoryDescription));
		list.add(session.createAccountCategory(categoryName+"2",categoryDescription));
		
		List<AccountCategory> categoryList = session.getAccountCategories();
		
		assertEquals(categoryList.size(),3);

		Iterator<AccountCategory> i = list.iterator();
		for(AccountCategory c:categoryList) {
			AccountCategory orig = i.next();
			System.out.println("testGetAccountCategories: " + c);
			assertEquals(orig,c);
		}
	}

	
	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.createAccount(String, String, AccountCategory)'
	 */
	public void testCreateAccountStringStringAccountCategory() {
		Account account = session.createAccount(accountName,accountDescription,AccountTypes.EXPENSES, new BigDecimal("1000"),new Date(), new Date(),true,session.createAccountCategory(categoryName+"1",categoryDescription));
		
		assertNotNull(account);
		
		assertTrue(account.getId() > 0);
		assertEquals(account.getName(),accountName);
		assertEquals(account.getDescription(),accountDescription);
		assertEquals(account.isEnabled(),true);

		accountId = account.getId();
		
		setComplete(); //do not rollback the transaction after this test
	}
	
	
	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.getAccount(int)'
	 */
	public void testGetAccount() {
		
		Account retrieved = session.getAccount(accountId);

		assertNotNull(retrieved);
		assertEquals(accountId,retrieved.getId());
	}

	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.createAccount(Account)'
	 */
	public void testCreateAccountAccount() {

		Account account = new AccountDAO();
		account.setDescription(accountDescription);
		account.setName(accountName+"1");
		
		
		account.setType(AccountTypes.INCOME);
		account.setCurrentBalance(new BigDecimal(0.00));
		account.setEnabled(true);
		
		account.setAccountCategory(session.createAccountCategory(categoryName+"2",categoryDescription));
		
		Account created = session.createAccount(account);

		assertEquals(created.getId(),account.getId());
		assertEquals(created,account);
	}
	
	/*
	 * Tests that an account can be created and linked with a parent account
	 */
	public void testCreateAccountParent() {
		Account parent = session.getAccount(accountId);
		Account account = session.createAccount(accountName+"1",accountDescription,AccountTypes.EXPENSES, new BigDecimal("1000"),new Date(), new Date(),true,session.createAccountCategory(categoryName+"2",categoryDescription), parent);

		session.flush();
		
		assertEquals(account.getParent(), parent);
		assertNotNull(parent.getChildren());
		assertEquals(1,parent.getChildren().size());
	}
	/*
	public void testGetTopLevelAccounts() {
		Account parent = session.getAccount(accountId);
		Account account = session.createAccount(accountName+"1",accountDescription,AccountTypes.EXPENSES, new BigDecimal("1000"),new Date(), new Date(),true,session.createAccountCategory(categoryName+"2",categoryDescription), parent);

		session.flush();

		List<Account> all = session.getAccounts(0, 0);
		List<Account> root = session.getRoot();
		
		
		//since there is only one child account, the difference between the top-level
		//accounts and all of the accounts should be 1
		assertEquals(root.size()+1,all.size());
	}*/

	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.updateAccount(Account)'
	 */
	public void testUpdateAccount() {
		Account account = session.getAccount(accountId);
		
		assertNotNull(account);
		
		account.setName("account1");
		session.updateAccount(account);
		
		assertEquals(account.getName(),"account1");
	}

	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.deleteAccount(int)'
	 */
	public void testDeleteAccountInt() {
		Account account = session.getAccount(accountId);
		session.deleteAccount(account.getId());
	}

	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.deleteAccount(Account)'
	 */
	public void testDeleteAccountAccount() {
		Account account = session.getAccount(accountId);
		session.deleteAccount(account);
	}

	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.enableAccount(int)'
	 */
	public void testEnableAccount() {
		Account account = session.getAccount(accountId);
		account.setEnabled(false);
		session.updateAccount(account);
		
		session.enableAccount(account.getId());
		assertTrue(account.isEnabled());
	}

	/*
	 * Test method for 'com.citep.db.hibernate.sessions.AccountSessionImpl.disableAccount(int)'
	 */
	public void testDisableAccount() {
		Account account = session.getAccount(accountId);
		session.disableAccount(account.getId());
		
		assertFalse(account.isEnabled());
	}

	public void testGetAccounts() {
		List<Account> list = session.getAccounts(0, 0);
		this.assertTrue(list.size() > 0);
	}

}
